<?php

class procKpiDayTask extends baseWemTask
{
    
    public function configure()
    {
        parent::configure();
        $this->name      = 'procKpiDay';
        $this->briefDescription = 'Process data to get the KPI of the Day';
                
        //ARGUMENTS
        $this->addArgument('plant',  sfCommandArgument::REQUIRED,'Plant to check');
        $this->addArgument('start',  sfCommandArgument::OPTIONAL,'The starting day [Format: Y-m-d]',0);
        $this->addArgument('days',  sfCommandArgument::OPTIONAL,'Number of days to check [the greater the range the longer the script..] ',5);
        
                //OPTIONS
        $this->addOptions(array(
            new sfCommandOption('overwrite', null, sfCommandOption::PARAMETER_NONE,'Force reprocessing and overwriting of existing kpiday'),));
        
    }
    public function execute($arguments = array(), $options = array())
    {
        
        //DB Connection
        $this->ConnectDB();
        //register task execution
        $tasklog=seth::regTaskStart($this->name, $arguments, $options );
        
        //get the requested plant
        $plant=new Plant();
        $plant=Doctrine_Core::getTable('plant')->find($arguments['plant']);        
        if (!is_object($plant)){
            $this->logBlock("The provided plant does not exist", "ERROR");
            seth::regTaskEnd($tasklog, false);
            die();
        }
        $plantName=$plant->getName();
        $this->options=$options;
        
        //add the log file
        $this->addFileLogger($plantName);

        /* 
         * TIME RANGE DEFINITION 
         */
        if ($arguments["start"]==0)
            $dStart=new DateTime();
        elseif (strtotime($arguments["start"]) == FALSE){
            $this->logBlock("Argument [start] provided is not valid. Must be a number >0","ERROR");
            seth::regTaskEnd($tasklog, false);
            die();
        }else
            $dStart=new DateTime($arguments["start"]);
            
        if (!is_numeric($arguments["days"]) or $arguments["days"]<=0){
            $this->logBlock("Argument [days] provided is not valid. Must be a number >0","ERROR");
            seth::regTaskEnd($tasklog, false);
            die();
        }else
            $days=$arguments["days"];

        /* RETRIEVES MODEL OBJECT TO BE USED */
        $meters=$plant->getMonitoredPowerDev(array("AcqSyst"=>"all","type"=>"Energy Meter","subtype"=>"GSE"));   
        $exch=$plant->getMonitoredPowerDev(array("AcqSyst"=>"all","type"=>"Energy Meter","subtype"=>"Exchange"));
        $weaths=$plant->getMonitoredPowerDev(array("AcqSyst"=>"all","type"=>"Weather"));
        
        $ts=microtime(true);
        $count=0;
        for ($i=1,$day=$dStart;$i<=$days;$i++){
            
            $recs=$plant->getKpiday(array("day"=>$day));
            //enter only if not exist a KPI for $day, or if $day is today, or if $option['overwrite'] is active

            if ( ($recs->count()== 0) or ($day->diff(new DateTime(),true)->days==0) or ($options['overwrite']) ){
                $this->logBlock("processing ".$plant->getName()." for day ".$day->format("Y/m/d"),"INFO");
                
                //TIME SETTING
                //look for the lowest (->oldest) last update between the devices
                //involved in the KPI processing
                
                $KpiLastUpdate=null;
                //check GSE meters
                foreach ($meters as $dev){
                     $devLastUpdate=$dev->getLastUpdate($day);
                     if (isset($devLastUpdate))
                        if (!isset($KpiLastUpdate) or ($KpiLastUpdate>$devLastUpdate))
                            $KpiLastUpdate=new DateTime($devLastUpdate->format("Y-m-d H:i:s"));         
                }    
                if ($options['debug'])
                    $this->logBlock("GSE meters last update: ".$KpiLastUpdate->format("Y-m-d H:i:s"),"INFO");
                
                //check Weather Sensors
                foreach ($weaths as $dev){
                     $devLastUpdate=$dev->getLastUpdate($day);
                     if (isset($devLastUpdate))
                        if (!isset($KpiLastUpdate) or ($KpiLastUpdate>$devLastUpdate))
                            $KpiLastUpdate=new DateTime($devLastUpdate->format("Y-m-d H:i:s"));  
                }
                if ($options['debug'])
                    $this->logBlock("Weather Sensor last update: ".$KpiLastUpdate->format("Y-m-d H:i:s"),"INFO");
                
                if (!isset($KpiLastUpdate))
                    $KpiLastUpdate=new DateTime($day->format("Y-m-d")." 23:45");
                 
                $to=$KpiLastUpdate;
                $from=new DateTime($to->format("Y-m-d")." 00:00");
                
                    
                
                /* 
                *  ENERGY PRODUCTED AND EXCHANGED
                */
                
                $this->logBlock("\t Processing GSE Meters from ".$from->format("Y-m-d H:i")." to ".$to->format("Y-m-d H:i"),"INFO");
                
                //$production is an array containings the production of each GSE Meters
                // GSE METER => PRODUCTION
                
                $production=$this->getEday($from, $to, $meters,"Prod");
                
                //SUM GSE METERS
                if (count($production)>1){
                    //if there are more than 1 GSE meter there also must be a virtual Tot GSE Meter
                    // with the sum of the others (subplant=NULL e "Tot" in the Label)
                    
                    //Certain Acquisition System already create the Tot GSE meter (Ex. Osiride)
                    //other do not
                    $found=false;
                    $tot=array();
                    foreach($production as $subPlant=>$en){
                            if ($subPlant=="Tot"){
                                $found=true;
                            }else{
                                
                                if (isset($tot["Eday"]))
                                    $tot["Eday"]+=$en["Eday"];
                                else
                                    $tot["Eday"]=$en["Eday"];
                               
                                if (!isset($tot["firstSamp"]) or ($tot["firstSamp"]>$en["firstSamp"])){
                                    $tot["firstSamp"]=$en["firstSamp"];
                                }
                                
                                if (!isset($tot["lastSamp"]) or ($tot["lastSamp"]<$en["lastSamp"])){
                                    $tot["lastSamp"]=$en["lastSamp"];
                                }
                            }
                    }
                        
                    if (!$found){
                        $production["Tot"]=$tot;
                        unset($tot);
                    }
                      
                }elseif (count($production)==0){
                
                       $this->logBlock("\t No GSE meters monitored in ".$plantName,"INFO");
                }
                
                /* 
                 * WEATHER
                *  Process the weather sensors to get:
                *     - Irradiance of the day [kWh]  
                 *    - Daily average Tmod
                 *    - Daily average Tenv 
                */
                
                $this->logBlock("\t computing Weather Sensors from ".$from->format("Y-m-d H:i")." to ".$to->format("Y-m-d H:i"),"INFO");
                $weathData=$this->getWeath($from, $to, $weaths);
                
                //average measure between available sensors
                if (count($weathData)>0){
                        $avrgRad=$this->measAvrg($weathData, "rad");
                        $avrgTmod=$this->measAvrg($weathData, "Tmod");
                        $avrgTenv=$this->measAvrg($weathData, "Tenv");
                        
                        $weathLastSamp=NULL;
                        foreach($weathData as $station){
                            if (isset($station["lastSamp"]))
                                if (!isset($weathLastSamp) or $weathLastSamp<$station["lastSamp"])
                                    $weathLastSamp=$station["lastSamp"];
                        }
                        
                    }else{
                        $this->logBlock("\t No Weather Station monitored in ".$plantName,"INFO");
                        $avrgRad=NULL;   
                    }
                 
                 $this->logBlock("\t Processing PR..","INFO");
               
                 foreach ($production as $subPlant=>$en){

                     // GET THE PLANT/SUBPLANT NOMINAL POWER
                    $pn=($subPlant=="Tot")?$plant->getPn():Doctrine_Core::getTable("Subplant")->find($subPlant)->getPn();
                    
                     //PERFORMANCE RATIO
                     if (($en["Eday"] !== NULL) and ($avrgRad !== NULL)  and ($avrgRad>0)){
                            $pr=$en["Eday"]/$avrgRad/$pn;
                     }else 
                        $pr=NULL;
                     
                     if ($en["Eday"]>0 or $avrgRad>0){
                         
                        $this->logBlock("\t Inserting KPI day ".$day->format("y-m-d").", Subplant ".$subPlant."...","INFO");
                        
                        //check if already exist a record for this subplant
                        $found=false;
                        if ($recs->count()>0){
                            foreach ($recs as $rec)
                                if (($subPlant=="Tot" and !($rec->getSubplant()->exists()) ) or ($rec->getSubplant()->getId() == $subPlant)){
                                $found=true;
                                break; 
                                }                               
                        }
                        if ($found)
                            $newKpi=$rec;
                        else{
                            $newKpi= new Kpiday();
                            $newKpi->setPlant($plant);  
                            if ($subPlant!="Tot")
                                $newKpi->setSubPlant($subPlant);
                            $newKpi->setDay($day->format("Y-m-d"));
                        }
                       
                        if (!is_null($en["lastSamp"]))
                            $newKpi->setLastupdate($en["lastSamp"]->format("Y-m-d H:i:s"));
                        else if(!is_null($weathLastSamp)){
                            $newKpi->setLastupdate($weathLastSamp->format("Y-m-d H:i:s"));
                        }else{
                            $now=new DateTime();
                            $newKpi->setLastupdate($now->format("Y-m-d"));
                        }
                        
                        //Production
                        if ($en["Eday"] !== NULL)
                            $newKpi->setExpen(round($en["Eday"],2));
                        else
                            $newKpi->setExpen(-1);

                        if (!is_null($avrgRad))
                            $newKpi->setRad(round($avrgRad,2));
                        else
                            $newKpi->setRad(-1);
                        
                        if (!is_null($avrgTenv))
                            $newKpi->setTenv(round($avrgTenv,2));
                        else
                            $newKpi->setTenv(-1);
                        
                        if (!is_null($avrgTmod))
                            $newKpi->setTmod(round($avrgTmod,2));
                        else
                            $newKpi->setTmod(-1);
  
                        if (!is_null($pr) and ($pr>=0) and ($pr<200))
                            $newKpi->setPr(round($pr,4));
                        else
                            $newKpi->setPr(-1);
                         
                        $newKpi->save();
                        if ($options['debug'])
                            $this->logBlock("\t Inserted  record in ".$newKpi->getPlant()." - ".$newKpi->getSubPlant().": PR=".$newKpi->getPr()." E=".$newKpi->getExpen()." Rad=".$newKpi->getRad()." Pn=".$pn, "INFO");
                            
                        $count++;
                        unset($newKpi);
                     }
                 }

            }
            //- 1 day
            $day->modify('-1 day');  
        }
        $this->logBlock("KPI process completed: inserted ".$count." records. Process took ".round((microtime(TRUE) - $ts),3)."s ", "INFO");
        
        unset($rec);
        $recs->free();
        unset($recs);
        
        seth::regTaskEnd($tasklog, TRUE);        
    }
    
    public function getEday($from, $to, $meters, $sign){
        
        if ($meters->count()>0){
            
                    $method="getDay".$sign;
                    
                    foreach ($meters as $meter){
                        
                        if ($this->options['debug'])
                            $this->logBlock("\t\t Processing ".$meter->getLabel(), "INFO");

                        $subPlantKey=($meter->getSubplant()!="")?$meter->getSubplant()->getId():"Tot";   
                        $Eday[$subPlantKey]=$meter->$method($from, $to);

                        if ($Eday[$subPlantKey] !== NULL){
                            $this->logBlock("\t\t Eday: From ".$Eday[$subPlantKey]['firstSamp']->format("Y-m-d H:i:s")." to ".$Eday[$subPlantKey]['lastSamp']->format("Y-m-d H:i:s")." Eday=".$Eday[$subPlantKey]['Eday']."kWh", "INFO");
                        }else{
                            $this->logBlock("\t\t No Data found for meter ".$meter->getLabel() ,"INFO");
                        }
                    }
        }else{
                   $Eday=NULL;
        }
          
          return $Eday;   
    }

    public function getWeath($from, $to, $weaths){
        if ($weaths->count()>0){   
                    foreach ($weaths as $key=>$weath){
                        if ($this->options['debug'])
                            $this->logBlock("\t\t Processing ".$weath->getLabel(), "INFO");
                        
                        //retrieve a collection of VarWeath records for the given time range ordered by SampleTime in ASC mode
                        $samples=$weath->getVar(array("from"=>$from, "to"=>$to));
                        
                        $rad=0;
                        $tMod=0;
                        $tEnv=0;
                        $sunnySamples=0;
                        if ($samples->count()>0){
                            foreach ($samples as $keyS=>$sample){
                                $currTime=new DateTime($sample->getSampletime());
                                
                                $nextTime=(isset($samples[$keyS+1]))?new DateTime($samples[$keyS+1]->getSampletime()):NULL;
                                if (isset($samples[$keyS+1]))
                                    $nextTime=new DateTime($samples[$keyS+1]->getSampletime());
                                else if (isset($samples[$keyS-1]))
                                    $nextTime=new DateTime($samples[$keyS-1]->getSampletime());
                                else
                                    $nextTime=null;
                                
                                if (isset($nextTime))
                                    $rad+=( $sample->getIrr()*($currTime->diff($nextTime,true)->format("%i")/60)/1000 ); // [kWh]
                                                           
                                if ($sample->getIrr()>10){
                                         //process temp only on sunny hours
                                        $sunnySamples++;
                                        $tMod+=$sample->getTmod();
                                        $tEnv+=$sample->getTenv();
                                    }
                            }
                            if ($sunnySamples>0){
                                $tMod=$tMod/$sunnySamples;
                                $tEnv=$tEnv/$sunnySamples;
                            }
                        }else{

                             $rad=NULL;
                             $currTime=NULL;
                             $tMod=null;
                             $tEnv=null;
                        } 

                        $subPlantKey=($weath->getSubplant()!="")?$weath->getSubplant()->getId():"Tot".($key+1);  
                        $meas[$subPlantKey]["rad"]=round($rad,2);
                        $meas[$subPlantKey]["Tmod"]=round($tMod,2);
                        $meas[$subPlantKey]["Tenv"]=round($tEnv,2);

                        if ($currTime !== NULL)
                            $meas[$subPlantKey]["lastSamp"]=$currTime;
                        else
                            $meas[$subPlantKey]["lastSamp"]=NULL;
                        
                        if ($rad !== NULL)
                                $this->logBlock("\t\t RAD: Until ".$meas[$subPlantKey]["lastSamp"]->format("Y-m-d H:i:s")." Rad=".$rad." kWh/mq", "INFO");
                            else
                                $this->logBlock("\t\t No Data found for weather station ".$weath->getLabel() ,"INFO");
                    }
        }else{
             $meas=NULL;
        }
            
        
        return $meas;
    }
    
    public function measAvrg($meas,$var){
           $j=0;
           if (count($meas)>0){
               $avrg=0;
               foreach ($meas as $m){       
                            if (is_numeric($m[$var]) and $m[$var]>0){
                                $avrg+=$m[$var];
                                $j++;
                            }    
               }
                if ($j>0){
                   $avrg=$avrg/$j;   
                   return $avrg;
                }
           }
           return NULL;
           
    }
}